package com.sikaryofficial.system.controller.report;

import com.sikaryofficial.common.core.constant.ReportConstant;
import com.sikaryofficial.common.core.domain.R;
import com.sikaryofficial.common.core.exception.ServiceException;
import com.sikaryofficial.common.core.web.controller.BaseController;
import com.sikaryofficial.system.api.model.ManualExportReq;
import com.sikaryofficial.system.api.model.user.UserDailyDTO;
import com.sikaryofficial.system.api.model.user.UserRegisterDTO;
import com.sikaryofficial.system.domain.dto.TrendDTO;
import com.sikaryofficial.system.domain.dto.TrendReq;
import com.sikaryofficial.system.manager.report.ReportManager;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;
import java.time.temporal.WeekFields;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 日活Controller
 *
 * @author qinjinyuan
 * @date 2023-10-27
 */
@RestController
@RequestMapping("/statistics")
@Api(tags = "运营统计报表")
@Slf4j
public class ReportController extends BaseController {
    @Autowired
    private ReportManager reportManager;

    private static int calculateWeekNumber(Date date) {
        LocalDate localDate = convertUtilDateToLocalDate(date);
        return localDate.get(WeekFields.ISO.weekOfWeekBasedYear());
    }

    private static int extractYear(Date date) {
        LocalDate localDate = convertUtilDateToLocalDate(date);
        // 获取 ISO 周历中的周数和年份
        int weekOfYear = localDate.get(WeekFields.ISO.weekOfWeekBasedYear());
        int year = localDate.getYear();
        // 如果是第1周且日期在1月1日之前，则属于上一年
        if (weekOfYear == 1 && localDate.getMonthValue() == 12) {
            return year + 1;
        }
        // 如果是第52或53周且日期在12月之后，则属于下一年
        if ((weekOfYear == 52 || weekOfYear == 53) && localDate.getMonthValue() == 1) {
            return year - 1;
        }
        return localDate.getYear();
    }

    private static LocalDate convertUtilDateToLocalDate(Date date) {
        return date.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate();
    }

    @PostMapping("/retention/trend")
    @ApiOperation("留存率-趋势")
    public R<List<TrendDTO>> retentionTrend(@RequestBody TrendReq req) {
        checkParam(req);
        // 参数处理 : 根据结束时间，计算出 endQuarter, endQuarter 年份*100 + 季度数
        handleReq(req);
        // 数据 查询
        return R.ok(reportManager.retentionTrend(req));
    }

    /**
     * 日活跃数
     *
     * @return
     */
    @PostMapping("/activity/trend")
    @ApiOperation("日活-趋势")
    public R<List<TrendDTO>> activityDailyTrend(@RequestBody TrendReq req) {
        checkParam(req);
        // 参数处理 : 根据结束时间，计算出 endQuarter, endQuarter 年份*100 + 季度数
        handleReq(req);
        // 数据 查询
        return R.ok(reportManager.activityDailyTrend(req));
    }

    @PostMapping("/queryRegisterDailyByTime")
    @ApiOperation("查询注册数据")
    public R<List<UserRegisterDTO>> queryRegisterDailyByTime(@RequestBody ManualExportReq req) {
        return R.ok(reportManager.queryRegisterDailyByTime(req));
    }

    @PostMapping("/queryDailyByTime")
    @ApiOperation("查询日活数据")
    public R<List<UserDailyDTO>> queryDailyByTime(@RequestBody ManualExportReq req) {
        return R.ok(reportManager.queryDailyByTime(req));
    }

    /**
     * 日活跃数
     *
     * @return
     */
    @PostMapping("/register/trend")
    @ApiOperation("新增用户-趋势")
    public R<List<TrendDTO>> registerDailyTrend(@RequestBody TrendReq req) {
        checkParam(req);
        // 参数处理 : 根据结束时间，计算出 endQuarter, endQuarter 年份*100 + 季度数
        handleReq(req);
        // 数据 查询 处理
        return R.ok(reportManager.registerDailyTrend(req));
    }

    private void checkParam(TrendReq req) {
        boolean checkTypeFlag = ReportConstant.DAY_TYPE.equals(req.getTimeType())
                || ReportConstant.WEEK_TYPE.equals(req.getTimeType())
                || ReportConstant.MONTH_TYPE.equals(req.getTimeType())
                || ReportConstant.QUARTER_TYPE.equals(req.getTimeType())
                || ReportConstant.YEAR_TYPE.equals(req.getTimeType());
        if (Boolean.FALSE.equals(checkTypeFlag)) {
            throw new ServiceException("Time type is error.");
        }
        if (Objects.isNull(req.getStartTime()) || Objects.isNull(req.getEndTime())) {
            throw new ServiceException("StartTime or EndTime is empty.");
        }
    }

    private void handleReq(TrendReq req) {
        Date endTime = req.getEndTime();
        Date startTime = req.getStartTime();
        int endYear = extractYear(endTime);
        int startYear = extractYear(startTime);
        if (ReportConstant.QUARTER_TYPE.equals(req.getTimeType())) {
            LocalDate endDate = convertUtilDateToLocalDate(endTime);
            LocalDate startDate = convertUtilDateToLocalDate(startTime);
            int endQuarter = endYear * 100 + calculateQuarter(endDate);
            int startQuarter = startYear * 100 + calculateQuarter(startDate);
            log.info("End Quarter: {}", endQuarter);
            log.info("Start Quarter: {}", startQuarter);
            req.setEndQuarter(endQuarter);
            req.setStartQuarter(startQuarter);
        }
        if (ReportConstant.WEEK_TYPE.equals(req.getTimeType())) {
            int endWeek = calculateWeekNumber(endTime);
            int startWeek = calculateWeekNumber(startTime);
            int endWeekWithYear = endYear * 100 + endWeek;
            int startWeekWithYear = startYear * 100 + startWeek;

            req.setEndWeek(endWeekWithYear);
            req.setStartWeek(startWeekWithYear);
            log.info("End Week (YYYYWW): {}", endWeekWithYear);
            log.info("Start Week (YYYYWW): {}", startWeekWithYear);

        }
    }

    private int calculateQuarter(LocalDate date) {
        return (date.getMonthValue() - 1) / 3 + 1;
    }
}
